home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 7684 / 7684.xpi / resources / fmPlaylist.js < prev    next >
Text File  |  2009-11-20  |  11KB  |  350 lines

  1. /**
  2.  * Copyright (c) 2008, Jose Enrique Bolanos, Jorge Villalobos
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions are met:
  7.  *
  8.  *  * Redistributions of source code must retain the above copyright notice,
  9.  *    this list of conditions and the following disclaimer.
  10.  *  * Redistributions in binary form must reproduce the above copyright notice,
  11.  *    this list of conditions and the following disclaimer in the documentation
  12.  *    and/or other materials provided with the distribution.
  13.  *  * Neither the name of Jose Enrique Bolanos, Jorge Villalobos nor the names
  14.  *    of its contributors may be used to endorse or promote products derived
  15.  *    from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  20.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
  21.  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  22.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  23.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  24.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  25.  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  26.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  **/
  29.  
  30. var EXPORTED_SYMBOLS = [];
  31.  
  32. const Cc = Components.classes;
  33. const Ci = Components.interfaces;
  34. const Ce = Components.Exception;
  35.  
  36. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  37. Components.utils.import("resource://firefm/fmCommon.js");
  38. Components.utils.import("resource://firefm/fmEntities.js");
  39.  
  40. /**
  41.  * Represents the currently playing playlist.
  42.  */
  43. FireFM.Playlist = {
  44.    // Topic notifications sent from this object.
  45.   get TOPIC_TRACK_VIDEO_LOADED() { return "firefm-track-video-loaded"; },
  46.  
  47.   /* Logger for this object. */
  48.   _logger : null,
  49.  
  50.   /* Playlist title. */
  51.   _title : null,
  52.   /* Array of tracks. */
  53.   _tracks : new Array(),
  54.   /* The track currently being played. */
  55.   _currentTrack : null,
  56.   /* Expiration of playlist (?). */
  57.   _expiry : null,
  58.  
  59.   /**
  60.    * Returns the title of the current playlist.
  61.    * @return the title of the current playlist.
  62.    */
  63.   get title() {
  64.     this._logger.trace("[getter] title");
  65.  
  66.     return this._title;
  67.   },
  68.  
  69.   /**
  70.    * Returns the expiration of the current playlist.
  71.    * @return the expiration of the current playlist.
  72.    */
  73.   get expiry() {
  74.     this._logger.trace("[getter] expiry");
  75.  
  76.     return this._expiry;
  77.   },
  78.  
  79.   /**
  80.    * Returns the track that is currently being played.
  81.    * @return the track that is currently being played.
  82.    */
  83.   get currentTrack() {
  84.     // XXX: no logging here for performance reasons.
  85.     return this._currentTrack;
  86.   },
  87.  
  88.   /**
  89.    * Gets the next track on the playlist.
  90.    * @return Track object with the next track to play. Returns null if there are
  91.    * no more tracks to play in this playlist.
  92.    */
  93.   getNextTrack : function() {
  94.     this._logger.debug("getNextTrack");
  95.     this._currentTrack = null;
  96.  
  97.     if (this.hasMoreTracks()) {
  98.       this._currentTrack = this._tracks.shift();
  99.  
  100.       // Try to obtain the video for this track, which is embedded in the
  101.       // track's page.
  102.       let that = this;
  103.       FireFM.Remote.getTrackVideo(
  104.         this._currentTrack,
  105.         function(aTrack, aVideo) { that._getTrackVideoLoad(aTrack, aVideo); });
  106.     }
  107.  
  108.     return this._currentTrack;
  109.   },
  110.  
  111.   /**
  112.    * Handles the response from the FireFM.Remote.getTrackVideo call. Sets the
  113.    * video for the current track if it still the same.
  114.    * @param aTrack The track for which the video was obtained.
  115.    * @param aVideo The HTML code of the embedded video from the track's page.
  116.    */
  117.   _getTrackVideoLoad : function(aTrack, aVideo) {
  118.     this._logger.trace("_getTrackVideoLoad");
  119.  
  120.     if (null != aVideo && "" != aVideo && aTrack.id == this._currentTrack.id) {
  121.       this._currentTrack.video = aVideo;
  122.  
  123.       FireFM.obsService.notifyObservers(
  124.         null, this.TOPIC_TRACK_VIDEO_LOADED, null);
  125.     }
  126.   },
  127.  
  128.   /**
  129.    * Indicates if there are more tracks left in the playlist.
  130.    * @return true if there are more tracks left in the playlist, false
  131.    * otherwise.
  132.    */
  133.   hasMoreTracks: function() {
  134.     this._logger.debug("hasMoreTracks");
  135.  
  136.     return (0 < this._tracks.length);
  137.   },
  138.  
  139.   /**
  140.    * Sets a new playlist.
  141.    * @param aDocument the XML document that contains the playslist.
  142.    */
  143.   setNewPlaylist : function(aDocument) {
  144.     this._logger.debug("setNewPlaylist");
  145.  
  146.     let playlist = aDocument.getElementsByTagName("playlist")[0];
  147.     let playlistItems = playlist.childNodes;
  148.     let playlistItemCount = playlistItems.length;
  149.     let playlistItem;
  150.  
  151.     // reset the playlist before making any changes.
  152.     this.clearPlaylist();
  153.  
  154.     for (let i = 0; i < playlistItemCount; i++) {
  155.       playlistItem = playlistItems[i];
  156.  
  157.       switch (playlistItem.tagName) {
  158.         case "title":
  159.           this._title = FireFM.decodeFMString(playlistItem.textContent);
  160.           break;
  161.         case "link":
  162.           let relAtt = playlistItem.getAttribute("rel");
  163.  
  164.           if ("http://www.last.fm/expiry" == relAtt) {
  165.             this._expiry = playlistItem.textContent;
  166.           }
  167.  
  168.           break;
  169.         case "trackList":
  170.           let tracks = playlistItem.childNodes;
  171.           let trackCount = tracks.length;
  172.           let trackItem;
  173.  
  174.           for (let j = 0; j < trackCount; j++) {
  175.             trackItem = tracks[j];
  176.  
  177.             if ("track" == trackItem.tagName) {
  178.               this._tracks.push(this._createTrack(trackItem));
  179.             }
  180.           }
  181.  
  182.           this._logger.debug(
  183.             "setNewPlaylist. Track count: " + this._tracks.length);
  184.           break;
  185.       }
  186.     }
  187.   },
  188.  
  189.   /**
  190.    * Creates a Track object from a node that should correspond to a playlist
  191.    * track.
  192.    * @param aTrackNode the track node to convert to a Track object.
  193.    * @return Track object that corresponds to the information in the input node.
  194.    */
  195.   _createTrack : function(aTrackNode) {
  196.     this._logger.trace("_createTrack");
  197.  
  198.     let track = null;
  199.     let trackFields = new Array();
  200.     let trackChildren = aTrackNode.childNodes;
  201.     let extension = aTrackNode.getElementsByTagName("extension");
  202.     let trackChildrenCount = trackChildren.length;
  203.     let trackChild;
  204.  
  205.     for (let i = 0; i < trackChildrenCount; i ++) {
  206.       trackChild = trackChildren[i];
  207.  
  208.       switch (trackChild.tagName) {
  209.         case "identifier":
  210.           trackFields[0] = trackChild.textContent;
  211.           break;
  212.         case "location":
  213.           trackFields[1] = trackChild.textContent;
  214.           break;
  215.         case "title":
  216.           trackFields[2] = FireFM.decodeFMString(trackChild.textContent);
  217.           break;
  218.         case "recording":
  219.           trackFields[3] = trackChild.textContent;
  220.           break;
  221.         case "album":
  222.           trackFields[4] = FireFM.decodeFMString(trackChild.textContent);
  223.           break;
  224.         case "creator":
  225.           trackFields[5] = FireFM.decodeFMString(trackChild.textContent);
  226.           break;
  227.         case "duration":
  228.           trackFields[6] = trackChild.textContent;
  229.           break;
  230.         case "image":
  231.           trackFields[7] = trackChild.textContent;
  232.           break;
  233.  
  234.         // XXX: deprecated: all of these should go when the old version of the
  235.         // playlist format is no longer supported.
  236.         case "id":
  237.           trackFields[0] = trackChild.textContent;
  238.           break;
  239.         case "lastfm:trackauth":
  240.           trackFields[8] = trackChild.textContent;
  241.           break;
  242.         case "lastfm:albumId":
  243.           trackFields[9] = trackChild.textContent;
  244.           break;
  245.         case "lastfm:artistId":
  246.           trackFields[10] = trackChild.textContent;
  247.           break;
  248.         case "link":
  249.           let relAtt = trackChild.getAttribute("rel");
  250.  
  251.           switch (relAtt) {
  252.             case "http://www.last.fm/artistpage":
  253.               trackFields[11] = trackChild.textContent;
  254.               break;
  255.             case "http://www.last.fm/albumpage":
  256.               trackFields[12] = trackChild.textContent;
  257.               break;
  258.             case "http://www.last.fm/trackpage":
  259.               trackFields[13] = trackChild.textContent;
  260.               break;
  261.             case "http://www.last.fm/buyTrackURL":
  262.               trackFields[14] = trackChild.textContent;
  263.               break;
  264.             case "http://www.last.fm/buyAlbumURL":
  265.               trackFields[15] = trackChild.textContent;
  266.               break;
  267.             case "http://www.last.fm/freeTrackURL":
  268.               trackFields[16] = trackChild.textContent;
  269.               break;
  270.           }
  271.  
  272.           break;
  273.       }
  274.     }
  275.  
  276.     // add the extension nodes to the list. This only applies to the new
  277.     // playlist format.
  278.     if (0 < extension.length) {
  279.       trackChildren = extension[0].childNodes;
  280.       trackChildrenCount = trackChildren.length;
  281.  
  282.       for (let i = 0; i < trackChildrenCount; i ++) {
  283.         trackChild = trackChildren[i];
  284.  
  285.         switch (trackChild.tagName) {
  286.           case "recording":
  287.             trackFields[3] = trackChild.textContent;
  288.             break;
  289.           case "trackauth":
  290.             trackFields[8] = trackChild.textContent;
  291.             break;
  292.           case "albumId":
  293.             trackFields[9] = trackChild.textContent;
  294.             break;
  295.           case "artistId":
  296.             trackFields[10] = trackChild.textContent;
  297.             break;
  298.           case "artistpage":
  299.             trackFields[11] = trackChild.textContent;
  300.             break;
  301.           case "albumpage":
  302.             trackFields[12] = trackChild.textContent;
  303.             break;
  304.           case "trackpage":
  305.             trackFields[13] = trackChild.textContent;
  306.             break;
  307.           case "buyTrackURL":
  308.             trackFields[14] = trackChild.textContent;
  309.             break;
  310.           case "buyAlbumURL":
  311.             trackFields[15] = trackChild.textContent;
  312.             break;
  313.           case "freeTrackURL":
  314.             trackFields[16] = trackChild.textContent;
  315.             break;
  316.         }
  317.       }
  318.     }
  319.  
  320.     track =
  321.       new FireFM.Track(
  322.         trackFields[0], trackFields[1], trackFields[2], trackFields[3],
  323.         trackFields[4], trackFields[5], trackFields[6], trackFields[7],
  324.         trackFields[8], trackFields[9], trackFields[10], trackFields[11],
  325.         trackFields[12], trackFields[13], trackFields[14], trackFields[15],
  326.         trackFields[16]);
  327.  
  328.     return track;
  329.   },
  330.  
  331.   /**
  332.    * Clears the playlist.
  333.    */
  334.   clearPlaylist : function() {
  335.     this._logger.debug("clearPlaylist");
  336.     this._title = null;
  337.     this._tracks.splice(0, this._tracks.length);
  338.     this._currentTrack = null;
  339.     this._expiry = null;
  340.   }
  341. };
  342.  
  343. /**
  344.  * FireFM.Playlist constructor.
  345.  */
  346. (function() {
  347.   this._logger = FireFM.getLogger("FireFM.Playlist");
  348.   this._logger.debug("init");
  349. }).apply(FireFM.Playlist);
  350.